import { BusEvent, PropertyKey, PropertyDataType } from "@/constants/Enums";
import { modelService, mLinkService } from "@/modules/model/service";
import {
  PropertyRow,
  UpdateModelPropertyDto,
  DeletePropertyDto,
  GetOptionsDto,
  AddPropertyValueDto,
  UpdateValueSpecificationDto,
  AppendPropertyValueDto,
  CreateSpecification,
  DeleteSlotSpecificationDto,
  InstanceSlotData,
  BannerItem,
  UpdateEventTypeWhenNoTriggerDto
} from "./types";
import { UpdateSlotValueDto } from "@/modules/model/service/dto";
import { locale } from "@/util/locale";
import { htmlPropertyKeys, notHtmlPropertyKeys } from "./propertyEditors/config";
import { PropertyItemType } from "@/constants/constants";
import { MenuItem } from "@/constants/types";
import menuItemPool from "@/config/menuItemPool";
import { cloneDeep } from "lodash";

export class PropertyUtil {
  getUpdatePropertyDtoByPropertyRow(propertyRow: PropertyRow, projectId: string, updateValueSpecification?: UpdateValueSpecificationDto) {
    const updateModelPropDto: UpdateModelPropertyDto = {
      projectId,

      key: propertyRow.key,

      value: "",

      dataType: propertyRow.dataType,
      instanceId: propertyRow.modelId,
      instanceIds: propertyRow.modelIds,
      isMultiplicity: propertyRow.isMultiplicity,

      updateMethod: propertyRow.updateMethod,
      updateValueSpecification: updateValueSpecification
    };

    return updateModelPropDto;
  }
  getSelectOptionsDto(propertyRow: PropertyRow, projectId: string) {
    const getOptionsDto: GetOptionsDto = {
      instanceId: propertyRow.modelId,
      instanceIds: propertyRow.modelIds,
      key: propertyRow.key,
      moreBtnCondition: propertyRow.moreBtnCondition!,
      projectId,
      checkMethod: propertyRow.checkMethod!
    };
    return getOptionsDto;
  }
  getAddPropertyDto(propertyRow: PropertyRow, projectId: string, metaclass: any, applyStereotypes?: string[]) {
    const getOptionsDto: AddPropertyValueDto = {
      instanceId: propertyRow.modelId,
      instanceIds: propertyRow.modelIds,
      key: propertyRow.key,
      dataType: propertyRow.dataType,
      projectId,
      addMethod: propertyRow.addMethod,
      metaclass,
      applyStereotypes
    };
    return getOptionsDto;
  }
  getAppendPropertyDto(propertyRow: PropertyRow, projectId: string) {
    const getOptionsDto: AppendPropertyValueDto = {
      instanceId: propertyRow.modelId,
      key: propertyRow.key,
      dataType: propertyRow.dataType,
      projectId,
      appendMethod: "",
      value: ""
    };
    return getOptionsDto;
  }

  async updatePropertyAndFresh(
    propertyRow: PropertyRow,
    value: any,
    autoRefresh = true,
    updateValueSpecification?: UpdateValueSpecificationDto,
    projectId?: string
  ) {
    const lastProjectId = app.activeProject ? app.activeProject!.id : projectId;
    const dto = propertyUtil.getUpdatePropertyDtoByPropertyRow(propertyRow, lastProjectId!, updateValueSpecification);
    dto.value = value;
    // 有模型的属性编辑
    if (propertyRow.modelId) {
      await modelService.updateModelProperty(dto);
      if (autoRefresh) {
        app.$bus.emit(BusEvent.REFRESH_PROPERTY, propertyRow.modelId, propertyRow.key);

      }
    }

  }

  async updateEventType(projectId:string, propertyRow:PropertyRow, value:string, autoRefresh = true) {
    // const lastProjectId = app.activeProject ? app.activeProject!.id : projectId;
    const dto = propertyUtil.getUpdatePropertyDtoByPropertyRow(propertyRow, projectId) as unknown as UpdateEventTypeWhenNoTriggerDto;
    dto.value = [value];
    dto.transitionId = propertyRow.extraData!.transitionId!;
    await modelService.updateEventTypeWhenNoTrigger(dto);
    if (autoRefresh) {
      app.$bus.emit(BusEvent.REFRESH_PROPERTY, propertyRow.modelId, propertyRow.key);
    }

  }

  async updateProperty(dto: UpdateModelPropertyDto, autoRefresh = true) {
    await modelService.updateModelProperty(dto);
    if (autoRefresh) {
      app.$bus.emit(BusEvent.REFRESH_PROPERTY, dto.instanceId, dto.key);
    }
  }
  async getPropertyContextMenu(propertyRow: PropertyRow): Promise<MenuItem[]> {
    let menuItemsKeys = ["locateComment"];
    if (!app.activeProject?.config.online) {
      // 不是协同项目过滤掉批注
      menuItemsKeys = menuItemsKeys.filter(key => key !== "locateComment");
    }
    const menuItem: MenuItem[] = menuItemsKeys.map(key => cloneDeep(menuItemPool?.[key])).filter(Boolean) as MenuItem[];
    let locateComment = menuItem.find(item => item.value === "LocateComment");
    // 设置批注只读状态
    if (locateComment) {
      const isShowComment = app.projectManager.activeProject?.ui.isShowComment;
      if (!app.activeProject?.config.online || !isShowComment || !propertyRow.commentList?.length) {
        locateComment.disabled = true;
      }
      locateComment.icon = "statics/cowork/comment/LocateComment.svg";
    }
    return menuItem;
  }

  async appendPropertyAndFresh(propertyRow: PropertyRow, value: any, autoRefresh = true) {
    const dto = propertyUtil.getAppendPropertyDto(propertyRow, app.activeProject!.id);
    dto.value = value;
    await modelService.appendPropertyValue(dto);
    if (autoRefresh) {
      app.$bus.emit(BusEvent.REFRESH_PROPERTY, propertyRow.modelId, propertyRow.key);
    }
  }
  async appendPropertiesAndFresh(
    propertyRow: PropertyRow,
    rowValue: any,
    propertyColumn: PropertyRow,
    columnValue: any,
    autoRefresh = true
  ) {
    const dtoRow = propertyUtil.getAppendPropertyDto(propertyRow, app.activeProject!.id);
    dtoRow.value = rowValue;
    const dtoColumn = propertyUtil.getAppendPropertyDto(propertyColumn, app.activeProject!.id);
    dtoColumn.value = columnValue;
    const dtos = [dtoRow, dtoColumn];
    await modelService.appendPropertiesValues(dtos);
    if (autoRefresh) {
      app.$bus.emit(BusEvent.REFRESH_PROPERTY, propertyRow.modelId, propertyRow.key);
    }
  }

  async updatePropertySlotAndFresh(dto: CreateSpecification) {
    //
    let res;
    try {
      if (!dto.valueSpecificationId) {
        // 不存在valueSpecificationId 则新建并更新
        res = await modelService.createSpecification(dto);
      } else {
        // 存在 则更新
        res = await modelService.updateSpecification(dto);
      }
      if (res.code == 1000) {
        return res;
      } else {
        return false;
      }
    } catch (e: any) {
      if (e.code === 7004) {
        app.$notify({
          title: "多重性超出上限",
          type: "warning",
          message: e.message
        });
      } else {
        app.$notify({
          title: e.title || "",
          type: "error",
          message: e.message
        });
      }
    }
  }

  async deleteSpecification(dto: DeleteSlotSpecificationDto) {
    modelService.deleteSpecification(dto);
  }
  /**
   * 多选的属性，删除其中的一个
   * @param propertyRow
   * @param projectId
   * @param deleteInstanceId // 拥有的属性（要删除的模型）的id

   * @returns
   */
  getDeletePropertyDto(propertyRow: PropertyRow, projectId: string, deleteInstanceId: string, index: number | null = null) {
    const deletePropertyDto: DeletePropertyDto = {
      deleteInstanceId: deleteInstanceId,
      deleteMethod: propertyRow.deleteMethod!,
      instanceId: propertyRow.modelId,
      instanceIds: propertyRow.modelIds,
      key: propertyRow.key,
      projectId,
      index
    };
    return deletePropertyDto;
  }
  /**
   *
   * @param propertyRow
   * @param value
   */
  async deletePropertyAndRefresh(propertyRow: PropertyRow, deleteInstanceId: string, projectId?: string) {
    // 属性删除
    const lastProjectId = app.activeProject ? app.activeProject!.id : projectId;
    const dto = propertyUtil.getDeletePropertyDto(propertyRow, lastProjectId!, deleteInstanceId);
    await modelService.deletePropertyValue(dto);
    app.$bus.emit(BusEvent.REFRESH_PROPERTY, propertyRow.modelId);
  }
  async removePropertyAndRefresh(propertyRow: PropertyRow, deleteInstanceId: string, autoRefresh = true, index: number | null = null) {
    // 属性移除
    const dto = propertyUtil.getDeletePropertyDto(propertyRow, app.activeProject!.id, deleteInstanceId, index);
    if (dto.key === PropertyKey.ActivityNode_inPartition) {
      dto.deleteMethod = "updateInPartitionByProperty";
    }
    await modelService.removePropertyValue(dto);
    if (autoRefresh) {
      app.$bus.emit(BusEvent.REFRESH_PROPERTY, propertyRow.modelId);
    }
  }
  async addPropertyValueAndRefresh(propertyRow: PropertyRow, metaclass: string, applyStereotypes?: string[]) {
    // 属性新增
    const dto = propertyUtil.getAddPropertyDto(propertyRow, app.activeProject!.id, metaclass, applyStereotypes);
    await modelService.addPropertyValue(dto);
    app.$bus.emit(BusEvent.REFRESH_PROPERTY, propertyRow.modelId);
  }
  async getSelectOptions(
    propertyRow: PropertyRow,
    page: number,
    size: number,
    keyword: string,
    projectId: string,
    classifierIds?: string[]
  ) {
    // 获取选择项
    const dto = propertyUtil.getSelectOptionsDto(propertyRow, projectId);
    if (!classifierIds) {
      classifierIds = [];
    }
    let res = await modelService.getSelectOptions({ ...dto, page, size, keyword }, classifierIds);
    // await sleep(0.3);

    return res;
  }
  async getModelicaSelectOptions(
    propertyRow: PropertyRow,
    page: number,
    size: number,
    keyword: string,
    projectId: string,
    classifierIds?: string[]
  ) {
    // 获取选择项
    const dto = propertyUtil.getSelectOptionsDto(propertyRow, projectId);
    if (!classifierIds) {
      classifierIds = [];
    }
    let res = await mLinkService.getSelectOptions({ ...dto, page, size, keyword }, classifierIds);
    // await sleep(0.3);

    return res;
  }
  async getDefaulteValue(instances: { instanceId: string; keys: string[] }[], projectId: string) {
    // 批量查询模型属性值
    let res = await modelService.getDefaulteValue(instances, projectId);
    return res;
  }

  async updateSlotValue(projectId: string, instanceId: string, row: InstanceSlotData, values: (string | number | boolean | null)[]) {
    const dto: UpdateSlotValueDto = {
      dataType: row.dataType,
      instanceId: instanceId,
      isMultiplicity: row.isMultiplicity!,
      key: row.propertyId,
      projectId: projectId,
      updateMethod: "",
      value: values
    };
    await modelService.updateSlotValue(dto);
  }
  /**
   * 获取属性翻译后的名称
   * 内置属性走配置的propertyConfig翻译，外置属性为数字，走i18翻译
   * @param key 属性key
   * @param name 属性名称
   * @returns
   */
  getTranslatedPropertyName(key: string | undefined, name: string) {
    if (key && key.charCodeAt(0) > 47 && key.charCodeAt(0) < 58) {
      return name;
    } else {
      return locale.getTByKey("propertyConfig." + key, name);
    }
  }
  /**
   *
   */
  isRowDataTypeEnum(row: Pick<PropertyRow, "dataType" | "itemType">) {
    // return row.dataType === PropertyDataType.ENUM || (row.dataType === PropertyDataType.INSTANCE && Array.isArray(row.items));
    return (
      row.dataType === PropertyDataType.ENUM || (row.dataType === PropertyDataType.INSTANCE && row.itemType === PropertyItemType.items)
    );
  }

  isHtmlTextProperty(propertyKey: string, dataType: PropertyDataType) {
    if (dataType !== PropertyDataType.STRING) {
      return false;
    }
    const firstCharCode = propertyKey.charCodeAt(0);
    if (firstCharCode > 64 && firstCharCode < 91) {
      // 是大写字母开头的key，认为是内置属性
      return htmlPropertyKeys.has(propertyKey);
    } else {
      // 非大写字母开头的属性，认为是自定义属性，自定义属性默认是允许html编辑，除非是在排除列表里的
      return !notHtmlPropertyKeys.has(propertyKey);
    }
  }
  /**
   * 把属性的value 转成banner信息
   */
  stringToBannerInfo(str: string): BannerItem | undefined {
    if (!str) {
      return;
    }
    try {
      const bannerItem = JSON.parse(str) as BannerItem;
      return bannerItem;
    } catch (error) {
      console.error("转换失败:", error);
    }
  }
}

export const propertyUtil = new PropertyUtil();
